package com.yy.hi.library.hilog;

import android.util.Log;

import org.jetbrains.annotations.NotNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * User: gongyongfeng2@yy.com
 * Date: 2020/6/12 10 :15
 */
public class HiFilePrinter implements HiLogPrint {
    private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
    private final String logPath;
    private final long retentionTime;
    private LogWriter mLogWriter;
    private volatile PrintWork mPrintWork;
    private static HiFilePrinter sHiFilePrinter;

    @Override
    public void print(@NotNull HiLogConfig config, int level, String tag,
                      @NotNull String printString) {
        long timeMills = System.currentTimeMillis();
        if (!mPrintWork.isRunning()) {
            mPrintWork.start();
        }
        mPrintWork.put(new HiLogMo(timeMills, level, tag, printString));
    }

    private HiFilePrinter(String logPath, long retentionTime) {
        this.logPath = logPath;
        this.retentionTime = retentionTime;
        this.mLogWriter = new LogWriter();
        this.mPrintWork = new PrintWork();
        cleanExpiredLog();
    }

    private void doPrint(HiLogMo mo) {
        String preFileName = mLogWriter.getPreFileName();
        if (preFileName == null) {
            String newFileName = genFileName();


            if (mLogWriter.isReady()) {
                mLogWriter.close();
            }

            if (!mLogWriter.ready(newFileName)) {
                return;
            }
        }
        mLogWriter.append(mo.flattened());
    }

    private void cleanExpiredLog() {
        if (retentionTime <= 0) {
            return;
        }
        long currentTimeMillis = System.currentTimeMillis();
        File logDir = new File(logPath);
        File[] files = logDir.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (currentTimeMillis - file.lastModified() > retentionTime) {
                file.delete();
            }
        }
    }

    private String genFileName() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yy-MM-dd", Locale.CHINA);
        dateFormat.setTimeZone(TimeZone.getDefault());
        return dateFormat.format(new Date(System.currentTimeMillis()));
    }

    public static synchronized HiFilePrinter getInstance(String logPath, long retentionTime) {
        if (sHiFilePrinter == null) {
            sHiFilePrinter = new HiFilePrinter(logPath, retentionTime);
        }
        return sHiFilePrinter;
    }

    private class PrintWork implements Runnable {

        private BlockingQueue<HiLogMo> mLogMos = new LinkedBlockingDeque<>();
        private volatile boolean running;

        void put(HiLogMo log) {
            try {
                mLogMos.put(log);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        boolean isRunning() {
            synchronized (this) {
                return running;
            }
        }

        void start() {
            synchronized (this) {
                EXECUTOR_SERVICE.execute(this);
                running = true;
            }
        }

        @Override
        public void run() {
            HiLogMo logMo;
            while (true) {
                try {
                    logMo = mLogMos.take();
                    doPrint(logMo);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    synchronized (this) {
                        running = false;
                    }
                }
            }

        }
    }

    private class LogWriter {
        private String preFileName;
        private File logFile;
        private BufferedWriter bufferedWriter;

        boolean isReady() {
            return bufferedWriter != null;
        }

        public String getPreFileName() {
            return preFileName;
        }

        boolean ready(String newFileName) {
            preFileName = newFileName;
            logFile = new File(logPath, newFileName);
            if (!logFile.exists()) {
                try {
                    File parent = logFile.getParentFile();
                    if (!parent.exists()) {
                        parent.mkdirs();
                    }
                    logFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                    preFileName = null;
                    logFile = null;
                    return false;
                }
            }

            try {
                bufferedWriter = new BufferedWriter(new FileWriter(logFile, true));
            } catch (IOException e) {
                e.printStackTrace();
                preFileName = null;
                logFile = null;
                return false;
            }
            return true;
        }

        boolean close() {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    return false;
                } finally {
                    bufferedWriter = null;
                    preFileName = null;
                    logFile = null;
                }
            }
            return true;
        }

        void append(String flattenedLog) {
            try {
                bufferedWriter.write(flattenedLog);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
